﻿using System;
using System.Collections.Generic;
using System.Linq;
using CommonBehaviors.Actions;
using Portal.Core;
using Portal.Helpers;
using Portal.Managers;
using Portal.Settings;
using Styx;
using Styx.CommonBot;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Action = Styx.TreeSharp.Action;

namespace Portal.Specialisation
{
    static class Common
    {
        private static PortalSettings Settings { get { return PortalSettings.Instance; } }
        private static LocalPlayer Me { get { return StyxWoW.Me; } }

        public static Composite PreCombat()
        {
            return new Decorator(ret => !Me.IsMoving() && !Me.Mounted && !Me.IsDead && !Me.Combat && !Me.IsFlying && !Me.IsOnTransport && !Me.HasAnyAura("Food", "Drink"),
                new PrioritySelector(
                HandleMageArmor(),
                
                // General
                Spell.CastRaidBuff("Arcane Brilliance", ret => Settings.UseArcaneBrilliance),
                HandleManaGem(),
                HandleConjureRefreshments(),
                HandleEvocation()
                ));
        }

        public static Composite NonGcdCombat()
        {
            return new PrioritySelector(

                new Decorator(ret => Invisible, new ActionAlwaysSucceed()),
                new Decorator(ret => Me.CurrentMap.IsRaid && Me.CurrentTarget.IsCasting &&
                                    SpellLists.StopCastingOnTargetCastId.Contains(Me.CurrentTarget.CastingSpellId),
                                    new ActionAlwaysSucceed()),
                new Decorator(ret => Me.InBGOrArena() && Me.IsCasting && Me.CurrentTarget.HasDoNotDamageAura(), 
                    new Action(delegate
                        {
                            Log.Info("Target has a 'Do Not Damage' aura. Cancelling Cast.");
                            Log.LogToGame("Cancelling cast - target has a do not damage aura.");
                            SpellManager.StopCasting();
                            return RunStatus.Success;
                        })),

                HandlePoM(),
                HandleIceFloes(),
                HandleTemporalShield(),
                HandleBlazingSpeed(),
                HandleDeepFreeze()
                
                
                );
        }

        public static Composite Combat()
        {
            return new PrioritySelector(

                Spell.Cast("Polymorph", on => GetHealer(), ret => Settings.PresenceOfMindCondition == 1 && Me.HasAura("Presence of Mind") && GetHealer() != null),

                // Racials
                Racials.UseRacials(),

                // Professions
                HandleLifeblood(),

                // Items
                HandleHealthstones(),
                Item.UseTrinkets(),
                HandleManaGem(),
                HandleEngineerGloves(),

                // Class Abilities
                HandleIceBlock(),
                Spell.PreventDoubleCast("Spellsteal", 3, ret => Settings.UseSpellsteal && TargetHasStealableBuff()),
                HandleTotemKilling(),
                HandleDecurse(),
                HandleBlink(),
                HandleMirrorImage(),
                HandleFrostNova(),
                
                HandleConeOfCold(),
                HandleEvocation(),

                // Talents
                HandleIceBarrier(),
                HandleRingOfFrost(),
                HandleIceWard(),
                HandleFrostjaw(),
                HandleColdSnap(),
                HandleIncantersWard()
                );
        }


        #region Ability Composites

        private static Composite HandleHealthstones()
        {
            return new PrioritySelector(
                Item.UseBagItem("Healthstone", ret => Settings.UseHealthstones && Me.Combat && Me.HealthPercent < Settings.HealthstoneHPPercent, "HP Low")
                );
        }

        private static Composite HandleEngineerGloves()
        {
            return new PrioritySelector(
                /* Synapse Springs
                    0 = Never
                    1 = On Boss Or Player
                    2 = On Cooldown
                */
                new Decorator(
                    ret => Settings.UseSynapseSprings
                        && (Settings.SynapseSpringsCond == 1 && (Me.CurrentTarget.IsBoss || Me.CurrentTarget.IsPlayer && Me.CurrentTarget.IsHostile))
                        || (Settings.SynapseSpringsCond == 2),
                Item.UseEngineerGloves())

                );
        }

        private static Composite HandleLifeblood()
        {
            return new Decorator(ret => Settings.UseLifeblood,
                new PrioritySelector(
                /* 
                0 = On Low HP
                1 = On Boss Or Player
                2 = On Cooldown
                */
                    Spell.Cast("Lifeblood", ret => Settings.LifebloodCond == 0 && Me.HealthPercent <= Settings.LifebloodLowHPValue),
                    Spell.Cast("Lifeblood", ret => Settings.LifebloodCond == 1 && Me.CurrentTarget.IsBossOrPlayer()),
                    Spell.Cast("Lifeblood", ret => Settings.LifebloodCond == 2)
                ));
        }

        private static Composite HandleTotemKilling()
        {

            return new Decorator(ret => Settings.KillTotems,
                new PrioritySelector(
                Spell.Cast("Ice Lance", on => GetTotem(), ret => GetTotem() != null)
                ));
        }

        private static Composite HandleManaGem()
        {
            return new PrioritySelector(
                Item.UseBagItem(36799, ret => NeedUseManaGem, "Mana Gem"),
                Item.UseBagItem(81901, ret => NeedUseManaGem, "Brilliant Mana Gem"),
                Spell.PreventDoubleCast("Conjure Mana Gem", 120, ret => NeedCreateManaGem)
                );
        } 

        private static Composite HandleEvocation()
        {
            return new Decorator(ret => Me.Level >= 40,
                new PrioritySelector(

                    // Don't have a Tier 6 Talent
                    new Decorator(ret => !TalentManager.HasTalent(16) && !TalentManager.HasTalent(17) && !TalentManager.HasTalent(18),
                        Spell.PreventDoubleCast("Evocation", 2, ret => NeedEvocation)
                        ),

                    // Do have a Tier 6 Talent
                    new Decorator(ret => TalentManager.HasTalent(16) || TalentManager.HasTalent(17) || TalentManager.HasTalent(18),
                        new PrioritySelector(
                        Spell.PreventDoubleCastOnGround("Evocation", 2.5, loc => Me.Location, ret => Me != null && Me.IsValid && !Me.IsMoving() && !Me.HasAura("Alter Time") && NeedRuneofPower),
                        Spell.PreventDoubleCast("Evocation", 4, ret => !Me.IsMoving() &&  NeedInvocation),
                        Spell.PreventDoubleCast("Incanter's Ward", 1, on => Me, ret => NeedIncantersWard)
                        ))

                

                ));

        }

        private static Composite HandleDecurse()
        {
            return new PrioritySelector(
                Spell.Cast("Remove Curse", on => Me, ret => Settings.DecurseSelf && NeedDecurseSelf),
                Spell.Cast("Remove Curse", on => Me.FocusedUnit, ret => Settings.DecurseFocus && NeedDecurseFocus)
                );
        }

        private static Composite HandleBlink()
        {
            return new PrioritySelector(
                        // Low HP
                        Spell.Cast("Blink", ret => Settings.UseBlinkLowHp && Me.HealthPercent < Settings.BlinkLowHpValue),
                        // Stunned/Rooted
                        Spell.Cast("Blink", ret => Settings.UseBlinkStunnedRooted && 
                            (Me.Rooted || Me.Stunned || Me.HasAuraWithMechanic(WoWApplyAuraType.ModRoot, WoWApplyAuraType.ModStun)))
                
                    );
        }

        private static Composite HandleIceBlock()
        {
            return new PrioritySelector(
                Spell.Cast("Ice Block", ret => Settings.UseIceBlock && Me.HealthPercent < Settings.IceBlockLowHpValue && !Me.ActiveAuras.ContainsKey("Hypothermia"))
                );
        }

        private static Composite HandleMirrorImage()
        {
            return new Decorator(ret => Settings.MirrorImageCondition != 0,
                /*
                Never
                On Cooldown
                On Boss Or Player
                On Target Low HP
                */
                Spell.Cast("Mirror Image", ret =>
                Settings.MirrorImageCondition == 1 || 
                (Settings.MirrorImageCondition == 2 && Me.CurrentTarget.IsBossOrPlayer()) || 
                (Settings.MirrorImageCondition == 3 && Me.CurrentTarget.HealthPercent < Settings.MirrorImageTargetLowHpValue)));
        }

        private static Composite HandleFrostNova()
        {
            return new Decorator(ret => Settings.UseFrostNova,
                Spell.Cast("Frost Nova", 
                ret => ((Settings.OnlyFrostNovaWhenTargetingMe && Unit.NearbyAttackableUnits(Me.Location, 12).Any(u => u.IsTargetingMeOrPet)) ||
                    (!Settings.OnlyFrostNovaWhenTargetingMe && Unit.NearbyAttackableUnits(Me.Location, 12).Any()))));
        }

        private static Composite HandleDeepFreeze()
        {
            return new Decorator(ret => Settings.UseDeepFreeze,
                Spell.Cast("Deep Freeze"));
        }

        private static Composite HandleConeOfCold()
        {
            return new Decorator(ret => Settings.UseConeOfCold,
                Spell.Cast("Cone of Cold", 
                    ret => Me.CurrentTarget.Distance <= 12 && ((Settings.ConeOfColdOnlyWhenNotSlowed && !Me.CurrentTarget.IsBoss() && !Me.CurrentTarget.HasAuraWithMechanic(WoWApplyAuraType.ModDecreaseSpeed)) ||
                        !Settings.ConeOfColdOnlyWhenNotSlowed) && Me.IsSafelyFacing(Me.CurrentTarget)
                )
                );
        }

        private static Composite HandleMageArmor()
        {
            /*None
            Mage Armor
            Molten Armor
            Frost Armor*/
            return new Decorator(ret => Settings.MageArmor != 0,
                new PrioritySelector(
                Spell.PreventDoubleCast("Mage Armor", 5, ret => Settings.MageArmor == 1 && !Me.HasAura("Mage Armor")),
                Spell.PreventDoubleCast("Molten Armor", 5, ret => Settings.MageArmor == 2 && !Me.HasAura("Molten Armor")),
                Spell.PreventDoubleCast("Frost Armor", 5, ret => Settings.MageArmor == 3 && !Me.HasAura("Frost Armor"))
                ));
        }

        private static Composite HandleConjureRefreshments()
        {
            return new Decorator(ret => Settings.ConjureRefreshments,
                Spell.Cast("Conjure Refreshment", ret => !Me.Combat && Me.CarriedItems.FirstOrDefault(i => SpellLists.ConjuredRefreshments.Contains(i.Entry)) == null)
                );
        }


        #endregion

        #region Talent Composites

        #region Tier 1

        private static Composite HandlePoM()
        {
            /*
                0,On Target Low HP
                1,With Polymorph When Someone Healing
                2,On Cooldown
                3,On Boss Or Player
                4,Optimal Offensive DPS -- Handled by each routine
             */
            return new Decorator(ret => Settings.UsePresenceOfMind && TalentManager.HasTalent(1),
                    new PrioritySelector(
                        Spell.Cast("Presence of Mind", ret => Settings.PresenceOfMindCondition == 0 && Me.HealthPercent <= Settings.PresenceOfMindTargetLowHPValue),
                        Spell.Cast("Presence of Mind", ret => Settings.PresenceOfMindCondition == 1 && GetHealer() != null),
                        Spell.Cast("Presence of Mind", ret => Settings.PresenceOfMindCondition == 2),
                        Spell.Cast("Presence of Mind", ret => Settings.PresenceOfMindCondition == 3 && Me.CurrentTarget.IsBossOrPlayer())
                ));
        }

        private static Composite HandleBlazingSpeed()
        {
            return new PrioritySelector(
                Spell.Cast("Blazing Speed", ret => Settings.UseBlazingSpeed && TalentManager.HasTalent(2) && Me.IsMoving())
                );
        }

        private static Composite HandleIceFloes()
        {
            return new PrioritySelector(
                Spell.Cast("Ice Floes", ret => Settings.UseIceFloes && TalentManager.HasTalent(3) && Me.IsMoving())
                );
        }
        #endregion

        #region Tier 2
        private static Composite HandleTemporalShield()
        {
            return new PrioritySelector(
                Spell.Cast("Temporal Shield",
                    ret => Settings.UseTemporalShieldWhenCcd && TalentManager.HasTalent(4) && Me.IsCrowdControlled()),
                Spell.Cast("Temporal Shield",
                    ret => Settings.UseTemporalShieldLowHp && TalentManager.HasTalent(4) && Me.HealthPercent <= Settings.UseTemporalShieldLowHpValue)
                );
        }

        private static Composite HandleIceBarrier()
        {
            /*
                0,Always
                1,In Combat
                2,Low HP
                3,Never
             */
            return new Decorator(ret => Settings.IceBarrierCondition != 3 && TalentManager.HasTalent(6) && !Me.HasAura("Ice Barrier"),
                new PrioritySelector(
                Spell.Cast("Ice Barrier", ret => Settings.IceBarrierCondition == 0),
                Spell.Cast("Ice Barrier", ret => Settings.IceBarrierCondition == 1 && Me.Combat),
                Spell.Cast("Ice Barrier", ret => Settings.IceBarrierCondition == 2 && Me.HealthPercent <= Settings.IceBarrierLowHpValue)
                ));
        }
        #endregion

        #region Tier 3
        private static Composite HandleRingOfFrost()
        {
            /*
                On Cooldown (and target isn't boss)
                On Me When Unit In Melee Range
                On Target When <X> Units In Range
                Never
             * On Target When Stunned/Rooted/Slowed
            */
            return new Decorator(ret => Settings.RingOfFrostCondition != 3 && TalentManager.HasTalent(7),
                new PrioritySelector(
                    Spell.PreventDoubleCastOnGround("Ring of Frost", 45, on => Me.CurrentTarget.Location, 
                        ret => Settings.RingOfFrostCondition == 0 && Me.CurrentTarget != null && !Me.CurrentTarget.IsBoss()),

                    Spell.PreventDoubleCastOnGround("Ring of Frost", 45, on => Me.Location, 
                        ret => Settings.RingOfFrostCondition == 1 && Unit.NearbyAttackableUnitsAttackingUs(Me.Location, 10).Any()),

                    Spell.PreventDoubleCastOnGround("Ring of Frost", 45, on => Me.CurrentTarget.Location,
                        ret => Settings.RingOfFrostCondition == 2 && Me.CurrentTarget != null && Unit.NearbyAttackableUnitsAttackingUs(Me.CurrentTarget.Location, 10).Count() >= Settings.RingOfFrostUnitsNearTarget),

                    Spell.PreventDoubleCastOnGround("Ring of Frost", 45, on => Me.CurrentTarget.Location,
                        ret => Settings.RingOfFrostCondition == 4 && Me.CurrentTarget != null && (Me.CurrentTarget.IsRooted() || Me.CurrentTarget.IsStunned() || Me.CurrentTarget.HasAnyAura(SpellLists.SlowedRootedStunned)))
                ));
        }

        private static Composite HandleIceWard()
        {
            return new Decorator(ret => Settings.UseIceWard && TalentManager.HasTalent(8),
                new PrioritySelector(
                    Spell.Cast("Ice Ward", on => Me, ret => Settings.IceWardOnMe),
                    Spell.Cast("Ice Ward", on => Me.CurrentTarget.CurrentTarget,
                        ret =>  Settings.IceWardOnTargetsTarget && 
                                Me.CurrentTarget.CurrentTarget != null)
                ));
        }

        private static Composite HandleFrostjaw()
        {
            /*
                On Target When Casting
                On Anyone Casting
                On Anyone Casting Healing Spell
                Never
            */
            return new Decorator(ret => Settings.FrostjawCondition != 3 && TalentManager.HasTalent(9),
                new PrioritySelector(
                    Spell.Cast("Frostjaw", ret => Settings.FrostjawCondition == 0 && Me.CurrentTarget.IsCasting),
                    Spell.Cast("Frostjaw", on => GetCaster(), ret => Settings.FrostjawCondition == 1 && GetCaster() != null),
                    Spell.Cast("Frostjaw", on => GetHealer(), ret => Settings.FrostjawCondition == 2 && GetHealer() != null)

                ));
        }
        #endregion

        #region Tier 4
        private static Composite HandleColdSnap()
        {
            return new PrioritySelector(
                Spell.Cast("Cold Snap", ret => Settings.UseColdSnapLowHp && TalentManager.HasTalent(12) && Me.HealthPercent <= Settings.ColdSnapLowHpValue),
                Spell.Cast("Cold Snap", ret => Settings.UseColdSnapIceBlockOnCdAndUnder70Hp && TalentManager.HasTalent(12) && Spell.SpellOnCooldown("Ice Block") && Me.HealthPercent <= 70)
                );
        }
        #endregion

        #region Tier 5

        public static Composite HandleSingleTargetMageBombs()
        {
            return new PrioritySelector(
                Spell.PreventDoubleCast("Nether Tempest", 1.5, ret => NeedNetherTempest),
                Spell.PreventDoubleCast("Living Bomb", 1.5, ret => NeedLivingBomb && CanCastLivingBomb),
                Spell.Cast("Frost Bomb", ret => NeedFrostBomb)
                );
        }

        public static Composite HandleBombMultiDoT()
        {
            return new PrioritySelector(
                Spell.PreventDoubleMultiDoT("Nether Tempest", 1.5, Me, 40, 1, ret => TalentManager.HasTalent(13)),
                Spell.PreventDoubleMultiDoT("Living Bomb", 1.5, Me, 40, 1,
                                            ret => TalentManager.HasTalent(14) && CanCastLivingBomb),
                Spell.Cast("Frost Bomb", ret => NeedFrostBomb)
                );
        }

        #endregion

        #region Tier 6
        private static Composite HandleIncantersWard()
        {
            return new PrioritySelector(
                Spell.Cast("Incanter's Ward",
                    ret => Settings.UseIncantersWardOnLowHP && TalentManager.HasTalent(18) && Me.HealthPercent <= Settings.UseIncantersWardOnLowHPValue),
                
                Spell.Cast("Incanter's Ward",
                    ret => Settings.UseIncantersWardOnUnitsTargetingMe && TalentManager.HasTalent(18) && 
                    Unit.NearbyAttackableUnitsAttackingUs(Me.Location, 40).Count(u => u != null && u.IsTargetingMeOrPet) <= Settings.UseIncantersWardOnUnitsTargetingMeValue)
                );
        }
        #endregion

        #endregion Talent Composites

        #region Unit

        private static WoWUnit GetTotem()
        {
            var unitList = Unit.NearbyAttackableUnits(Me.Location, Me.HasAura("Metamorphosis") ? 20 : 40);

            return unitList.FirstOrDefault(u =>
                u.Entry == 5913 || // Tremor Totem
                u.Entry == 2523 || // Searing Totem
                u.Entry == 2630 || // Earthbind Totem
                u.Entry == 3527 || // Healing Stream Totem
                u.Entry == 5929 || // Magma Totem
                u.Entry == 5925 || // Grounding Totem
                u.Entry == 61245 || // Capacitor Totem
                u.Entry == 62002 // Stormlash Totem
                );

        }

        public static WoWUnit GetHealer()
        {
            var unitList = Unit.NearbyAttackableUnitsAttackingUs(Me.Location, 40);
            return unitList.FirstOrDefault(u => !u.IsPet && u.IsCastingHealingSpell && u.CanInterruptCurrentSpellCast && u.InLineOfSpellSight);
        }

        public static WoWUnit GetCaster()
        {
            var unitList = Unit.NearbyAttackableUnitsAttackingUs(Me.Location, 40);
            return unitList.FirstOrDefault(u => !u.IsPet && (u.IsCasting || u.IsChanneling) && u.CanInterruptCurrentSpellCast && u.InLineOfSpellSight);
        }

        #endregion

        #region Conditions
        public static int _nearbyAoEUnitCount;
        public static Composite SetCounts
        {
            get
            {
                return new Action(delegate
                {
                    try
                    {
                        if (Me.CurrentTarget != null)
                        {
                            var countList = Unit.NearbyAttackableUnits(Me.CurrentTarget.Location, 15);
                            _nearbyAoEUnitCount = countList != null ? countList.Count() : 0;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Debug("Unit detection failed with: {0}", ex);
                        _nearbyAoEUnitCount = 1;
                    }
                    return RunStatus.Failure;
                });
            }
        }
        public static bool LoSCheck { get { return ((Me.InBGOrArena()) && !Me.CurrentTarget.InLineOfSpellSight); } }
        public static bool Hasted { get { return StyxWoW.Me.HasAnyAura("Bloodlust", "Heroism", "Ancient Hysteria", "Time Warp"); } }
        private static bool Invisible { get { return Me.HasAura(66) || Me.HasAura(110959); } }
        private static bool CanPolyMorph { get
        {
            return Unit.NearbyAttackableUnits(Me.Location, 60)
                .FirstOrDefault(u => u.HasAnyOfMyAuras(SpellLists.PolymorphIds)) == null;
        } }

        private static bool NeedDecurseSelf { get { return Me.GetAllAuras().Any(a => a != null && a.IsHarmful && a.IsActive && a.Spell.DispelType == WoWDispelType.Curse); } }
        private static bool NeedDecurseFocus { get { return Me.FocusedUnit != null && Me.FocusedUnit.GetAllAuras().Any(a => a != null && a.IsHarmful && a.IsActive && a.Spell.DispelType == WoWDispelType.Curse); } }

        private static bool NeedUseManaGem { get { return Settings.UseManaGem && (Me.ManaPercent < Settings.ManaGemLowManaValue) && !Me.HasAura("Alter Time"); } }
        private static bool NeedCreateManaGem { get { return Settings.ConjureManaGem && !Me.Combat && Me.CarriedItems.FirstOrDefault(i => i.Entry == 36799 || i.Entry == 81901) == null; } }

        private static bool NeedEvocation { get
        {
            return (Settings.UseEvocationLowHP && (Me.HealthPercent < Settings.EvocationLowHPPercent))
                ||
                (Settings.UseEvocationLowMana && (Me.ManaPercent < Settings.EvocationLowManaPercent));
        }}

        private static bool CanCastLivingBomb { get { return Unit.NearbyAttackableUnits(Me.CurrentTarget.Location, 60).Count(x => x.HasMyAura("Living Bomb")) < 3; } }

        #region Tier 5 Talents
        private static bool NeedNetherTempest { get { return TalentManager.HasTalent(13) && (!Me.CurrentTarget.HasMyAura("Nether Tempest") || Spell.GetMyAuraTimeLeft(114923, Me.CurrentTarget) <= 1); } }
        private static bool NeedLivingBomb { get { return TalentManager.HasTalent(14) && !Me.CurrentTarget.HasMyAura(44457); } }
        private static bool NeedFrostBomb { get { return TalentManager.HasTalent(15); } }
        #endregion

        #region Tier 6 Talents
        private static bool NeedInvocation { get { return Settings.UseInvocation && TalentManager.HasTalent(16) && !Me.HasAura(116257); } }
        private static bool NeedRuneofPower { get { return Settings.UseRuneOfPower && TalentManager.HasTalent(17) && !Me.HasAura(116014) && !Me.IsMoving && !Me.IsOnTransport; } }
        private static bool NeedIncantersWard { get
        {
            return TalentManager.HasTalent(18) && 
                ((Settings.UseIncantersWardOnLowHP && Me.HealthPercent < Settings.UseIncantersWardOnLowHPValue) || 
                (Settings.UseIncantersWardOnUnitsTargetingMe 
                        && Unit.NearbyAttackableUnits(Me.Location, 40).Count(u => u.IsTargetingMeOrPet) >= Settings.UseIncantersWardOnUnitsTargetingMeValue));
        } }
        #endregion

        private static bool TargetHasStealableBuff()
        {
			if (Me.CurrentTarget == null)
				return false;
		
            using (StyxWoW.Memory.AcquireFrame())
            {
                for (int i = 1; i <= Me.CurrentTarget.GetAllAuras().Count(a => a != null && !a.IsPassive && !a.IsHarmful && a.IsActive); i++)
                {
                    try
                    {
                        List<string> luaRet =
                            Styx.WoWInternals.Lua.GetReturnValues(
                                String.Format(
                                    "local buffName, _, _, _, _, _, _, _, isStealable = UnitAura(\"target\", {0}, \"HELPFUL\") return isStealable,buffName",
                                    i));
                        if (luaRet != null && luaRet[0] == "1")
                        {
                            var stealableSpell = !Me.HasAura(luaRet[1]) && (luaRet[1] != "Arcane Brilliance" && luaRet[1] != "Dalaran Brilliance");
                            Log.Debug("Attempting to steal: {0}", luaRet[1] ?? "Unknown");
                            return stealableSpell;
                        }
                    }
                    catch
                    {
                        return false;
                    }
                }
                return false;
            }
        }

        #endregion Conditions


    }
}
